package org.mozhu.mboot.core.data.hibernate.type;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.TextType;
import org.hibernate.usertype.UserType;
import org.mozhu.mboot.core.json.JSON;
import org.mozhu.mboot.core.json.JSONParseException;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

/**
 * JSONListType
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class JSONListType implements UserType {

    private static final int[] SQL_TYPES = {Types.VARCHAR};

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }

    @Override
    public Object assemble(Serializable cached, Object object) throws HibernateException {
        return deepCopy(cached);
    }

    @Override
    public Serializable disassemble(Object object) throws HibernateException {
        return (Serializable) deepCopy(object);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null) {
            return null;
        } else if (!(value instanceof List)) {
            throw new IllegalArgumentException("the value " + value.getClass().getName() + " must be instance of " + List.class.getName());
        } else {
            return clone((List) value);
        }
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        return x.equals(y);
    }

    @Override
    public int hashCode(Object value) throws HibernateException {
        if (value == null) {
            return 31;
        } else {
            return value.hashCode();
        }
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
        String string = (String) TextType.INSTANCE.nullSafeGet(resultSet, names, session, owner);
        if (StringUtils.isBlank(string)) {
            return null;
        }
        try {
            return JSON.deserialize(new StringReader(string), List.class);
        } catch (JSONParseException e) {
            throw new RuntimeException("invalid data for json list");
        } catch (IOException ignored) {
        }
        return null;
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            statement.setNull(index, Types.VARCHAR);
        } else if (!(value instanceof List)) {
            throw new IllegalArgumentException("the value " + value.getClass().getName() + " must be instance of " + List.class.getName());
        } else {
            StringWriter writer = new StringWriter();
            try {
                JSON.serialize(writer, value);
            } catch (IOException ignored) {
            }
            statement.setString(index, writer.toString());
        }
    }

    private static List clone(List list) {
        if (list == null) {
            return null;
        }
        List clone = new ArrayList();
        clone.addAll(list);
        return clone;
    }

    @Override
    public Class<?> returnedClass() {
        return List.class;
    }

    @Override
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

}
